﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ComponentModel;

namespace TA.DotNetUtils
{
	//public class SimpleIdValueConverter<I, T> : TypeConverter
	//  where I : struct
	//  where T : IHasId
	//{
	//  public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
	//  {

	//    return sourceType.Equals(typeof(Int16)) || sourceType.Equals(typeof(Int32)) || sourceType.Equals(typeof(Int64))
	//      || sourceType.Equals(typeof(UInt16)) || sourceType.Equals(typeof(UInt32)) || sourceType.Equals(typeof(UInt64))
	//      || sourceType.Equals(typeof(Byte)) || sourceType.Equals(typeof(SByte))
	//      || sourceType.Equals(typeof(Guid));
	//  }
	//  public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
	//  {
	//    I id;
	//    id = (I)Convert.ChangeType(value, typeof(I));
	//    return new SimpleIdValue<I, T>(id);
	//  }
	//}
	//[TypeConverter(typeof(SimpleIdValueConverter<I, T>))]
	public class SimpleIdValue<I, T> : IHasGenericId//, IConvertible
		where I : struct
		where T : IHasId<I>
	{
		I _id;
		T _value;

		public SimpleIdValue(I id)
		{
			Id = id;
		}
		public SimpleIdValue(T value)
		{
			Value = value;
		}
		public virtual I Id
		{
			get
			{
				return _id;
			}
			set
			{
				if (!object.Equals(_id, value))
				{
					_id = value;
					_value = default(T);
				}
			}
		}
		public T Value
		{
			get
			{
				if (_value == null)
				{
					MethodInfo getter = null;
					object res = null;
					if (getter == null)
					{
						getter = typeof(T).GetMethod("Get", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(I) }, new ParameterModifier[0]);
					}
					if (getter == null)
					{
						getter = typeof(T).GetMethod("Get", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(I?) }, new ParameterModifier[0]);
					}
					if (getter != null)
					{
						res = getter.Invoke(null, new object[] { this._id });
					}
					else
					{
						Type type = Type.GetType(typeof(T).AssemblyQualifiedName.Replace(typeof(T).FullName, typeof(T).FullName + "Provider"));
						if (type != null)
						{
							getter = type.GetMethod("Get", new Type[] { typeof(I) });
						}
						if (getter != null)
						{
							res = getter.Invoke(Activator.CreateInstance(type), new object[] { this._id });
						}
					}
					if (res is T)
					{
						this._value = (T)res;
					}
					else if (res is IEnumerable<T>)
					{
						this._value = ((IEnumerable<T>)res).FirstOrDefault<T>();
					}
				}
				return _value;
			}
			set
			{
				if (value == null) throw new ArgumentNullException();
				Id = (I)value.GenericId;
				_value = value;
			}
		}
		public bool HasValue
		{
			get
			{
				return _value != null;
			}
		}
		public object GenericId
		{
			get
			{
				return Id;
			}
			set
			{
				if (value is I)
					Id = (I)value;
				else
					throw new ArgumentException();
			}
		}
		public static implicit operator SimpleIdValue<I, T>(I id)
		{
			return new SimpleIdValue<I, T>(id);
		}
		public static implicit operator SimpleIdValue<I, T>(T value)
		{
			if (value == null)
				return null;
			return new SimpleIdValue<I, T>(value);
		}
		public static implicit operator I(SimpleIdValue<I, T> idValue)
		{
			if (idValue == null) return default(I);
			return idValue.Id;
		}
		public static implicit operator I?(SimpleIdValue<I, T> idValue)
		{
			if (idValue == null) return null;
			return idValue.Id;
		}
		public override string ToString()
		{
			return _id.ToString();
		}
		public override int GetHashCode()
		{
			return Id.GetHashCode();
		}
		public bool Equals(SimpleIdValue<I, T> obj)
		{
			return obj != null && this.Id.Equals(obj.Id);
		}
		public override bool Equals(object obj)
		{
			if (obj is SimpleIdValue<I, T> && object.Equals(this.Id, ((SimpleIdValue<I, T>)obj).Id))
				return true;
			if(obj is I &&object.Equals(this.Id, obj))
				return true;
			if(obj is T && object.Equals(this.Id, ((T)obj).Id))
				return true;
			return false;
		}
	}
}
